﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using BuzzwordBingo.Interface;

namespace BuzzwordBingo.Entities
{
    /// <summary>
    /// Bingo event, implements IBingoEvent, PersitenceBase
    /// </summary>
    public class BingoEvent : PersistenceBase, IBingoEvent
    {
        #region private members

        private Random _randomGenerator;
        private List<int> _indicesList;
        private IParticipant _winner;

        #endregion

        #region constructors

        /// <summary>
        /// Initialize bingo event
        /// </summary>
        public BingoEvent()
        {
            ParticipantList = new List<IParticipant>();
            _indicesList = new List<int>();
            _winner = null;
        }

        #endregion

        #region IBingoEvent Members

        public DateTime StartDate { get; set; }
        public DateTime EndDate { get; set; }
        public string Speaker { get; set; }
        public string Organiser { get; set; }
        public IBuzzwordCategory Category { get; set; }
        public IList<IParticipant> ParticipantList { get; set; }
        public int GetId { get { return base.Id; } }

        public event EventHandler Bingo;

        public void AddEventDates(DateTime Start, DateTime End)
        {
            if (Start.CompareTo(End) < 0)
            {
                StartDate = Start;
                EndDate = End;
            }
            else
                throw new EntitiesException("'start date' not less than 'end date'");
        }

        public void AddBuzzWord(IBuzzWord NewBuzzWord)
        {
            if (Category == null)
                Category = new BuzzwordCategory("none");
            Category.BuzzWordList.Add(NewBuzzWord);
        }

        public void AddParticipant(IParticipant NewParticipant)
        {
            ParticipantList.Add(NewParticipant);
        }

        public void CreateBingoCard(IParticipant ForParticipant)
        {
            if (Category == null || Category.BuzzWordList.Count < 25)
            {
                throw new EntitiesException("buzzword list must contain at least 25 words");
            }
            if (ParticipantList.Contains(ForParticipant))
            {
                IBingoCard card = new BingoCard();
                _indicesList.Clear();

                for (int k = 0; k < 5; k++)
                {
                    for (int j = 0; j < 5; j++)
                    {
                        card.BuzzWords[j, k] = new BuzzWord(Category.BuzzWordList[GetIndexNumber()].FuzzyWord);
                    }
                }

                foreach (var result in ParticipantList.Where(p => p == ForParticipant))
                {
                    result.MyBingoCard = card;
                }
            }
            else
                throw new EntitiesException("participant not in list");
        }

        public void MarkBuzzWord(IParticipant OfParticipant, int Index)
        {
            foreach (var result in ParticipantList.Where(p => p == OfParticipant))
                result.MyBingoCard.MarkBuzzWord(Index);

            foreach (var p in ParticipantList)
            {
                if (p.MyBingoCard != null && p.MyBingoCard.IsComplete)
                {
                    _winner = p;
                    OnBingo(new EventArgs());
                    break;
                }
            }
        }

        public IBingoCard GetBingoCard(IParticipant OfParticipant)
        {
            IBingoCard card = null;
            foreach (var result in ParticipantList.Where(p => p == OfParticipant))
                card = result.MyBingoCard;

            return card;
        }

        public IParticipant GetWinner()
        {
            return _winner;
        }

        #endregion

        #region public methods

        /// <summary>
        /// Randomly get an index number of the buzzword category list
        /// </summary>
        /// <returns>index number</returns>
        public int GetIndexNumber()
        {
            int number = 0;
            int index = 0;
            int randomMax = Category.BuzzWordList.Count - 1;

            if (_randomGenerator == null)
                _randomGenerator = new Random();

            // build indices
            if (_indicesList.Count == 0)
                for (int i = 0; i < Category.BuzzWordList.Count; i++)
                    _indicesList.Add(i);

            // return the remaining index in the list
            // or randomly select one, then remove it from the list
            if (_indicesList.Count == 1)
            {
                number = _indicesList[0];
                _indicesList.RemoveAt(0);
            }
            else
            {
                index = _randomGenerator.Next() % (_indicesList.Count - 1);
                number = _indicesList[index];
                _indicesList.RemoveAt(index);
            }

            return number;
        }

        #endregion

        #region private methods

        /// <summary>
        /// Fires 'bingo' event
        /// </summary>
        /// <param name="e">event args</param>
        private void OnBingo(EventArgs e)
        {
            if (Bingo != null)
                Bingo(this, e);
        }

        #endregion
    }
}
